/**
 * @file bsp_gpio.c
 * @brief 
 * @author TuxMonkey (nqx2004@gmail.com)
 * @version 1.0
 * @date 2025-07-08
 * 
 * @copyright Copyright (c) 2025  DLMU-C.ONE
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2025-07-08 <td>1.0     <td>TuxMonkey     <td>内容
 * </table>
 */

#include "bsp_gpio.h"
#include "memory.h"
#include "stdlib.h"

static uint8_t idx;
static GPIOInstance *gpio_instance[GPIO_MX_DEVICE_NUM] = {NULL};

/**
 * @brief EXTI中断回调函数,根据GPIO_Pin找到对应的GPIOInstance,并调用模块回调函数(如果有)
 * @note 如何判断具体是哪一个GPIO的引脚连接到这个EXTI中断线上?
 *       一个EXTI中断线只能连接一个GPIO引脚,因此可以通过GPIO_Pin来判断,PinX对应EXTIX
 *       一个Pin号只会对应一个EXTI,详情见gpio.md
 * @param GPIO_Pin 发生中断的GPIO_Pin
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    // 如有必要,可以根据pinstate和HAL_GPIO_ReadPin来判断是上升沿还是下降沿/rise&fall等
    GPIOInstance *gpio;
    for (size_t i = 0; i < idx; i++)
    {
        gpio = gpio_instance[i];
        if (gpio->GPIO_Pin == GPIO_Pin && gpio->gpio_model_callback != NULL)
        {
            gpio->gpio_model_callback(gpio);
            return;
        }
    }
}

/**
 * @brief GPIO注册
 * @param  GPIO_config      GPIO配置结构体
 * @return * GPIOInstance*  指向新注册的GPIO实例
 * @note 该函数会将GPIO配置结构体中的信息复制到一个新的GPIOInstance实例中,
 *       并将其添加到全局的gpio_instance数组中,以便后续使用。
 *       注意:该函数不会对GPIO进行初始化,只会注册一个GPIO实例,
 *       具体的GPIO初始化需要在其他地方进行。
 */
GPIOInstance *GPIORegister(GPIO_Init_Config_s *GPIO_config)
{
    GPIOInstance *ins = (GPIOInstance *)malloc(sizeof(GPIOInstance));
    memset(ins, 0, sizeof(GPIOInstance));

    ins->GPIOx = GPIO_config->GPIOx;
    ins->GPIO_Pin = GPIO_config->GPIO_Pin;
    ins->pin_state = GPIO_config->pin_state;
    ins->exti_mode = GPIO_config->exti_mode;
    ins->id = GPIO_config->id;
    ins->gpio_model_callback = GPIO_config->gpio_model_callback;
    gpio_instance[idx++] = ins;
    return ins;
}

// ----------------- GPIO API -----------------
// 都是对HAL的形式上的封装,后续考虑增加GPIO state变量,可以直接读取state
/**
 * @brief 切换GPIO电平
 * @param  _instance        GPIO实例
 */
void GPIOToggle(GPIOInstance *_instance)
{
    HAL_GPIO_TogglePin(_instance->GPIOx, _instance->GPIO_Pin);
}

/**
 * @brief 设置GPIO电平为高
 * @note 该函数会将GPIO引脚设置为高电平,即输出逻辑1。
 *       该函数通常用于控制外设或LED等需要输出高电平的场景。
 *       注意:该函数不会检查GPIO引脚的当前状态,直接将其设置为高电平。
 *       如果需要根据当前状态进行判断,请使用GPIORead函数。
 * @param  _instance        GPIO实例
 * @return * void
 */

void GPIOSet(GPIOInstance *_instance)
{
    HAL_GPIO_WritePin(_instance->GPIOx, _instance->GPIO_Pin, GPIO_PIN_SET);
}

/**
 * @brief   GPIO引脚复位
 *       注意:该函数不会检查GPIO引脚的当前状态,直接将其设置为低电平。
 *       如果需要根据当前状态进行判断,请使用GPIORead函数。
 * @param  _instance        GPIO实例
 * @return * void
 */

void GPIOReset(GPIOInstance *_instance)
{
    HAL_GPIO_WritePin(_instance->GPIOx, _instance->GPIO_Pin, GPIO_PIN_RESET);
}

/**
 * @brief 读取GPIO引脚状态
 * @note 该函数会读取GPIO引脚的当前状态,并返回其电平状态。
 *       如果需要根据当前状态进行判断,请使用此函数。
 * @param _instance         GPIO实例
 * @return GPIO_PinState    GPIO引脚的电平状态,可能是GPIO_PIN_SET或GPIO_PIN_RESET
 */

GPIO_PinState GPIORead(GPIOInstance *_instance)
{
    return HAL_GPIO_ReadPin(_instance->GPIOx, _instance->GPIO_Pin);
}
